package websocket

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/os/gmlock"
	"github.com/gogf/gf/v2/os/grpool"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/pkg/errors"
	"websocket/internal/consts"
	"websocket/utility"
)

type wManager struct {
	operateLockKey string
	workPool       *grpool.Pool
	enable         bool
	shutdown       bool
}

var (
	insManager = wManager{
		operateLockKey: "socket:manager:lock:",
		workPool:       grpool.New(1024),
		enable:         true,
		shutdown:       false,
	}
)

func Manager() *wManager {
	return &insManager
}

func (w *wManager) Shutdown(ctx context.Context) {
	gmlock.Lock("websocket:manager:shutdown")
	defer gmlock.Unlock("websocket:manager:shutdown")
	g.Dump("websocket manager shutdown start")
	w.shutdown = true
	w.SetEnable(ctx, false)
	w.CancelAll(ctx)
	g.Dump("websocket manager shutdown end")
}

func (w *wManager) SetEnable(ctx context.Context, enable bool) {
	gmlock.Lock("websocket:manager:enable")
	defer gmlock.Unlock("websocket:manager:enable")
	w.enable = enable
}

//ClientRegister 注册tcp连接
func (w *wManager) ClientRegister(unique consts.WebsocketUniqueType, conn *ghttp.WebSocket) error {
	if !w.enable {
		return errors.New("forbidden")
	}
	gmlock.Lock(w.operateLockKey + gconv.String(unique))
	defer gmlock.Unlock(w.operateLockKey + gconv.String(unique))
	ctx := gctx.New()
	ctx, cancel := context.WithCancel(ctx)
	//不要尝试在这g.Dump(c) 否则 哼哼
	c := &client{
		Closed:     false,
		Unique:     unique,
		Ipaddr:     conn.RemoteAddr().String(),
		Ctx:        ctx,
		writeCh:    make(chan []byte),
		WriteChNum: 0,
		ctxClose:   cancel,
		ConnTime:   gtime.Datetime(),
		conn:       conn,
	}
	c.heartbeat = c.Heartbeat()
	err := Assemble().SetClient(ctx, unique, conn, c)
	if err != nil {
		return err
	}
	go c.read()
	go c.write()
	//g.Dump("connect:" + gconv.String(unique))
	return nil
}

//ClientCancel 注销连接
func (w *wManager) ClientCancel(ctx context.Context, unique int64) {
	//上锁 防止重复关闭
	gmlock.Lock(w.operateLockKey + gconv.String(unique))
	defer gmlock.Unlock(w.operateLockKey + gconv.String(unique))
	Assemble().RemoveClient(ctx, unique, true)
}

//CancelAll 注销所有连接
func (w *wManager) CancelAll(ctx context.Context) {
	ctx = Context().InitLogger(ContextLogger{
		ClientTraceId: gctx.CtxId(ctx),
		UpTime:        gtime.Now(),
		Original:      "",
	}, ctx)
	//上锁 防止重复关闭
	clients := Assemble().GetClients(ctx)
	for unique := range clients {
		g.Dump(gconv.String(unique) + ":shutdown start")
		_ = w.WriteMsg(ctx, unique, w.BuildRes(ctx, &consts.WebsocketWorkRes{
			Cmd:    consts.WebsocketCmdSysShutdown,
			Code:   0,
			Msg:    "shutdown",
			Unique: unique,
			Data:   nil,
		}))
		gmlock.Lock(w.operateLockKey + gconv.String(unique))
		Assemble().RemoveClient(ctx, unique, false)
		gmlock.Unlock(w.operateLockKey + gconv.String(unique))
		g.Dump(gconv.String(unique) + ":shutdown end")
	}
}

func (w *wManager) WriteMsg(ctx context.Context, unique consts.WebsocketUniqueType, msg []*consts.WebsocketWorkRes) error {
	gmlock.Lock(w.operateLockKey + gconv.String(unique))
	defer gmlock.Unlock(w.operateLockKey + gconv.String(unique))
	c, err := Assemble().GetClientByUnique(ctx, unique)
	if err != nil {
		return utility.Common.BuildErr(ctx, consts.WebsocketStatusNotOnline, "unique is not online")
	}
	c.writeMsg(ctx, msg)
	return nil
}

func (w *wManager) BuildRes(ctx context.Context, msg *consts.WebsocketWorkRes, option ...*consts.WebsocketWorkRes) []*consts.WebsocketWorkRes {
	logger := Context().GetLogger(ctx)
	logger.FinishTime = gtime.Now()
	logger.RunTime, _ = utility.Math.Sub(ctx, gconv.String(logger.FinishTime.TimestampMilli()), gconv.String(logger.UpTime.TimestampMilli()))
	g.Log().Cat("res").Info(ctx, logger)
	msg.ExecDatetime = utility.Common.BuildDatetime()
	msg.TraceId = gctx.CtxId(ctx)
	if len(option) == 0 {
		option = []*consts.WebsocketWorkRes{}
	}
	option = append(option, msg)
	return option
}
